package com.cas.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;

@Component
public class NettyServer {
    private static final Logger logger = LoggerFactory.getLogger("data-access");

    @Resource
    private NettyServerHandler nettyServerHandler;

    @Value("${netty.port}")
    private int port;

    public void serverRun() {

        //创建辅助工具类，用于服务器通道的一系列配置。
        //一个 Netty 应用通常由一个 Bootstrap 开始，主要作用是配置整个 Netty 程序，串联各个组件。
        //Bootstrap 类是客户端程序的启动引导类，ServerBootstrap 是服务端启动引导类。
        ServerBootstrap bootstrap = new ServerBootstrap();

        //服务端要建立两个group线程池：
        //一个是负责接收客户端连接的线程池
        //bossGroup就是parentGroup，是负责处理TCP/IP连接的
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);

        //一个负责处理数据传输的线程池
        //workerGroup就是childGroup,是负责处理Channel(通道)的I/O事件
        EventLoopGroup workerGroup = new NioEventLoopGroup(10);
        try {
            // 使用ServerBootstrap引导类绑定线程池
            bootstrap.group(bossGroup, workerGroup)
                    // 本channel使用非阻塞NIO方式设置channel
                    .channel(NioServerSocketChannel.class)
                    // 设置channel（通道）的option（选项）。
                    // ChannelOption.SO_BACKLOG设置主要是针对boss线程组。
                    // 设置TCP可连接队列数。服务端接受连接的队列长度，如果队列已满，客户端连接将被拒绝。
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    // 保持长连接，可以在设置的读写超时时间范围内保持连接
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    // 非延迟，有数据立刻发送
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    // new ChannelInitializer<SocketChannel>() 用途当一个新的连接被接受时，
                    // 一个新的子 Channel 将会被创建，而 ChannelInitializer 将会把一个
                    // TcpServerNettyHandlerBiz 的实例添加到该 Channel 的 ChannelPipeline 中。
                    // 绑定客户端连接时候触发操作
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
//                            socketChannel.pipeline().addLast("delimiter", new DelimiterBasedFrameDecoder(65536, Unpooled.copiedBuffer("##".getBytes(CharsetUtil.UTF_8))));
                            socketChannel.pipeline().addLast("delimiter", new LineBasedFrameDecoder(8192));
                            socketChannel.pipeline().addLast("decoder",new StringDecoder(StandardCharsets.UTF_8));
                            socketChannel.pipeline().addLast("encoder",new StringEncoder(StandardCharsets.UTF_8));
                            socketChannel.pipeline().addLast("handler",nettyServerHandler);
                        }
                    });
            //绑定端口，同步等待成功
            ChannelFuture channelFuture = bootstrap.bind(port).sync();
            if(channelFuture.isSuccess()){
                logger.info("[TcpServer]-$<启动成功>$");
            }
            //等待服务端监听端口关闭
            channelFuture.channel().closeFuture().sync();

        } catch (Exception e) {
            logger.error("[TcpServer]-$<启动失败>$", e);
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
